/** @file    NetworkChannelTcpServer.cpp
 *  @author  Alessandro Polo
 *  @version $Id: NetworkChannelTcpServer.cpp 2879 2010-08-09 02:41:13Z alex $
 *  @brief
 * File containing methods for the wosh::network::NetworkChannelTcpServer class.
 * The header for this class can be found in NetworkChannelTcpServer.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/network/NetworkChannelTcpServer.h>
 #include <core/ObjectFactory.h>


using namespace std;
namespace wosh {
 namespace network {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

NetworkChannelTcpServer::NetworkChannelTcpServer( const std::string& name )
	: wosh::ThreadImpl(NULL, "wosh.Network.NetworkChannelTcpServer") {
	if ( name == "" ) {
		setThreadName("NetworkChannelTcpServer");
		Log.setContext( "NetworkChannelTcpServer" );
	 }
	else {
		setThreadName(name);
		Log.setContext( name );
	 }
	Log.setIndent( 4 );
	Log.setLevel( LOG_INFO );
//Log.setLevel( LOG_VERBOSE );

	this->tcpServer = NULL;
	this->listener = NULL;
 }

NetworkChannelTcpServer::~NetworkChannelTcpServer() {
	if ( this->isListening() )
		stopListening();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool NetworkChannelTcpServer::initializingThread() {
	this->tcpServer = ObjectFactory::createTypeOf<wosh::network::ServerTcp>(false);
	if ( this->tcpServer == NULL ) {
		Log(LOG_CRITICAL, ":initializingThread() FAILED Creating ServerTcp!" );
		return false;
	 }
	this->tcpServer->setListener(this);
	bool set = this->tcpServer->setAddressPort( this->bindAddressPort );
	if ( !set ) {
		Log(LOG_CRITICAL, ":initializingThread() FAILED setting bind address '%s'!", this->tcpServer->getAddressPort().c_str() );
		delete this->tcpServer; this->tcpServer = NULL;
		return false;
	 }
	Log(LOG_VERBOSE, ":initializingThread() Binding TCP server to '%s'..", this->tcpServer->getAddressPort().c_str() );
	WRESULT ret = this->tcpServer->bindSocket();
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":initializingThread() FAILED binding address '%s'!", this->tcpServer->getAddressPort().c_str() );
		delete this->tcpServer; this->tcpServer = NULL;
		return false;
	 }
	return true;
 }

void NetworkChannelTcpServer::runThread() {
	Log(LOG_VERBOSE, ":runThread() Entering QT Exec.." );
	exec();
 }

void NetworkChannelTcpServer::exitingThread() {
	Log(LOG_VERBOSE, ":exitingThread() UnBinding TCP server.." );
	WRESULT ret = this->tcpServer->unbindSocket();
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":exitingThread() FAILED#%d UnBinding Server to %s [%d]", ret, this->tcpServer->getAddressPort().c_str(), (int)this->tcpServer->isListening() );
	 }
	Log(LOG_VERBOSE, ":exitingThread() Destroying TCP server.." );
	WRESULT destroyed = ObjectFactory::destroy(this->tcpServer);
	if ( WFAILED(destroyed) ) {
		Log(LOG_CRITICAL, ":exitingThread() FAILED#%d Destroying Server", destroyed );
	 }
	this->tcpServer = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkChannelTcpServer::listen( const std::string& addressport, unsigned long timeout ) {
	if ( this->tcpServer != NULL && this->tcpServer->isListening() ) {
		return WRET_ERR_ILLEGAL_USE;
	 }
	this->bindAddressPort = addressport;
	Log(LOG_VERBOSE, ":listen(%s,%lu) ", this->bindAddressPort.c_str(), timeout );
	WRESULT ret = startThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":listen(%s,%lu) Listening FAILED#%d [TimedOut]!", this->bindAddressPort.c_str(), timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":listen(%s) Asynch Listening..", this->bindAddressPort.c_str() );
		else
			Log(LOG_INFO, ":listen(%s) Listening succedded.", this->bindAddressPort.c_str() );
	 }
	return ret;
 }

WRESULT NetworkChannelTcpServer::stopListening( unsigned long timeout ) {
	if ( !isThreadRunning() ) {
		Log(LOG_CRITICAL, ":stopListening() Not Running/Listening!" );
		return WRET_OK;
	 }
	Log(LOG_VERBOSE, ":stopListening(%lu) ", timeout );
	WRESULT ret = ThreadImpl::quitThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":stopListening(%lu) FAILED#%d [TimedOut]!", timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":stopListening() Asynch Disconnecting.." );
		else
			Log(LOG_INFO, ":stopListening() Disconnecting succedded." );
	 }
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkChannelTcpServer::serverTcp_incomingConnection( int socket_id, wosh::network::ServerTcp*  ) {
	if ( socket_id == 0 ) {
		Log(LOG_WARNING, ":serverTcp_incomingConnection() NULL incoming Socket!" );
		return;
	 }
	Log(LOG_INFO, ":serverTcp_incomingConnection() Incoming connection on socket#%d", socket_id );
	if ( this->listener != NULL )
		this->listener->channelTcpServer_incomingConnection(this, socket_id);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool NetworkChannelTcpServer::isListening() const {
	if ( !this->isThreadRunning() ) return false;
	if ( this->tcpServer == NULL ) return false;
	return this->tcpServer->isListening();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkChannelTcpServer::setListener( wosh::network::INetworkChannelTcpServerListener* tcp_listener ) {
	this->listener = tcp_listener;
	setOwnerObject( dynamic_cast<wosh::Object*>(tcp_listener) );
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace network
}; // namespace wosh
